#ifndef __J1939_TP_H__
#define __J1939_TP_H__

#include <FreeRTOS.h>
#include <timers.h>

#include "canframe.h"
#include "macro_math.h"

/*PGN definition*/
#define J1939TP_CM_PGN 0xEC00
#define J1939TP_DT_PGN 0xEB00

#define J1939_TP_CMD_RTS 	0x10		/* 发送请求 byte0 */
#define J1939_TP_CMD_CTS 	0x11		/* 接收应答 byte0 */
#define J1939_TP_CMD_EOMA 	0x13		/* 接收完成 byte0 */
#define J1939_TP_CMD_BAM 	0x20		/* 广播命令 byte0 */
#define J1939_TP_CMD_ABORT 	0xff		/* 连接终止 */

#define SIZE2PACKETS(size) ((size) / 7 + (((size) % 7) ? 1 : 0))
#define GET_J1939_SOURCE_ADDR(canid) ((canid)&0xFF)
#define GET_J1939_PRIORITY(canid) (((canid) >> 26) & 0x07)
#define J1939_PGN_PDU1_MAX 0x3ff00
#define J1939_PGN_MAX 0x3ffff
#define J1939_MAX_UNICAST_ADDR 0xfd
#define J1939_IDLE_ADDR 0xfe /*null address*/
#define J1939_NO_ADDR 0xff	 /* == broadcast or no addr */
#define J1939_BROADCAST_ADDR 0xFF

#define J1939_TX_GLOBAL_MSG 	1
#define J1939_TX_SPECIFIC_MSG 	2
#define J1939TP_TASK_PRIORITY 	3

#define xTaskCheckTickTimeout(endtick) ((int)(xTaskGetTickCount() - (endtick)) >= 0)

/* J1939 Parameter Group Number
 *
 * bit 0-7	: PDU Specific (PS)
 * bit 8-15	: PDU Format (PF)
 * bit 16	: Data Page (DP)
 * bit 17	: Reserved (R)
 * bit 19-31	: set to zero
 */
typedef uint32_t pgn_t;
/* J1939 Priority
 *
 * bit 0-2	: Priority (P)
 * bit 3-7	: set to zero
 */
typedef uint8_t priority_t;

/* J1939 NAME
 *
 * bit 0-20	: Identity Number
 * bit 21-31	: Manufacturer Code
 * bit 32-34	: ECU Instance
 * bit 35-39	: Function Instance
 * bit 40-47	: Function
 * bit 48	: Reserved
 * bit 49-55	: Vehicle System
 * bit 56-59	: Vehicle System Instance
 * bit 60-62	: Industry Group
 * bit 63	: Arbitrary Address Capable
 */
typedef uint64_t name_t;

enum j1939_tp_abort
{
	J1939_TP_NO_ABORT = 0,
	J1939_TP_ABORT_BUSY = 1,
	/* Already in one or more connection managed sessions and
	 * cannot support another.
	 *
	 * EALREADY:
	 * Operation already in progress
	 */

	J1939_TP_ABORT_RESOURCE = 2,
	/* System resources were needed for another task so this
	 * connection managed session was terminated.
	 *
	 * EMSGSIZE:
	 * The socket type requires that message be sent atomically,
	 * and the size of the message to be sent made this
	 * impossible.
	 */

	J1939_TP_ABORT_TIMEOUT = 3,
	/* A timeout occurred and this is the connection abort to
	 * close the session.
	 *
	 * EHOSTUNREACH:
	 * The destination host cannot be reached (probably because
	 * the host is down or a remote router cannot reach it).
	 */

	J1939_TP_ABORT_GENERIC = 4,
	/* CTS messages received when data transfer is in progress
	 *
	 * EBADMSG:
	 * Not a data message
	 */

	J1939_TP_ABORT_FAULT = 5,
	/* Maximal retransmit request limit reached
	 *
	 * ENOTRECOVERABLE:
	 * State not recoverable
	 */

	J1939_TP_ABORT_UNEXPECTED_DATA = 6,
	/* Unexpected data transfer packet
	 *
	 * ENOTCONN:
	 * Transport endpoint is not connected
	 */

	J1939_TP_ABORT_BAD_SEQ = 7,
	/* Bad sequence number (and software is not able to recover)
	 *
	 * EILSEQ:
	 * Illegal byte sequence
	 */

	J1939_TP_ABORT_DUP_SEQ = 8,
	/* Duplicate sequence number (and software is not able to
	 * recover)
	 */

	J1939_TP_ABORT_MESSAGE_SIZE_EXCEED_LIMIT = 9,
	/* Message size > 1785 bytes
	 * (TP)
	 */

	J1939_TP_ABORT_OTHER = 250,
	/* Any other reason (if a Connection Abort reason is
	 * identified that is not listed in the table use code 250)
	 */
};

typedef enum
{
	J1939TP_TX_IDLE,
	J1939TP_TX_LOCK_BUFFER,
	J1939TP_TX_WAIT_FOR_CTS,
	J1939TP_TX_SENDING_DT,
	J1939TP_TX_WAIT_FOR_ENDOFMSGACK
} j1939tp_tx_state_e;

typedef enum
{
	J1939TP_RX_IDLE,
	J1939TP_RX_WAIT_SEND_CTS,
	J1939TP_RX_RECEIVING_DT,
	J1939TP_RX_WAIT_SEND_ENDOFMSGACK,
} j1939tp_rx_state_e;
enum j1939_transfer_type
{
	J1939_TP,
	J1939_SIMPLE,
};

typedef struct
{
	/*name_t src_name;
	name_t dst_name;*/
	pgn_t pgn;

	uint8_t sa;
	uint8_t da;

	uint8_t type;
} j1939_addr_t;

typedef struct
{
	j1939_addr_t addr;
	priority_t priority;
} j1939_msg_info_t;

typedef struct
{
	uint8_t msg_code;
	union
	{
		struct can_frame frame;
		struct
		{
			uint8_t index;
			uint8_t type;
		};
	};
} j1939_output_msg_info_t;

typedef struct
{
	uint32_t end_tick;
	uint8_t valid;
	uint8_t timeout_type;
} j1939tp_internal_timer_t;

typedef struct j1939tp_tx_runtime_s
{
	uint32_t total_msg_size;
	uint8_t tx_total_packets;
	uint8_t waitForCTS_packets;
	uint8_t tx_max_packets;
	uint8_t retransmit_cnt;
	uint8_t state;
	j1939tp_internal_timer_t timer;
	/*we act as sender,below information*/
	uint32_t pgn;
	uint8_t priority;
	uint8_t daddr;
} j1939tp_tx_runtime_t;

typedef struct j1939tp_rx_runtime_s
{
	uint32_t total_msg_size;
	uint8_t rx_total_packets;
	uint8_t sendCTS_packets;
	uint8_t rx_max_packets;
	uint8_t state;
	j1939tp_internal_timer_t timer;
	/*we act as sender,below information*/
	uint32_t pgn;
	uint8_t priority;
	uint8_t saddr; /*extract the DA from received canid as SA,but ignore 0xFF*/
} j1939tp_rx_runtime_t;

typedef struct
{
	uint8_t *buffer;
	uint16_t max_len;
	uint16_t max_bam_len;
	j1939tp_tx_runtime_t runtime[2]; /*0: DA=local,1:DA=broadcast,0xFF*/
	uint8_t addr;
} j1939tp_tx_sesssion_t;

typedef struct
{
	uint8_t *buffer;
	uint16_t max_len;
	uint16_t max_bam_len;
	j1939tp_rx_runtime_t runtime[2]; /*0: DA=local,1:DA=broadcast,0xFF*/
	uint8_t addr;
} j1939tp_rx_sesssion_t;

typedef struct
{
	j1939tp_tx_sesssion_t *tx;
	j1939tp_rx_sesssion_t *rx;
	uint8_t tx_num;
	uint8_t rx_num;
} j1939tp_session_t;

typedef struct j1939_pgn_table_s
{
	pgn_t pgn;
	void (*handle_func)(const j1939_msg_info_t *info, const uint8_t *data, uint32_t length);
} j1939_pgn_table_t;

typedef struct j1939tp_config_s
{
	/*Timeout occurred on transmitter or receiver side,while trying to send the next TP.DT or TP.CM frame,default:50ms*/
	uint16_t timeout_Tr;
	uint16_t timeout_Th; /*default 500ms*/
	uint16_t timeout_T1; /*default 750ms*/
	uint16_t timeout_T2; /*default 1250ms*/
	uint16_t timeout_T3; /*default 1250ms*/
	uint16_t timeout_T4; /*default 1050ms*/
	void *rxqueue;		 //QueueHandle_t
	uint8_t *rxq_storage;
	void *txqueue;
	uint8_t *txq_storage;
	uint16_t rx_queue_size;
	uint16_t tx_queue_size;

	uint8_t padding;		/*default 0xFF*/
	uint8_t retransmit_max; /*default 2*/
	uint8_t tp_block;		/*default 0x16*/
	uint8_t chn;

	const j1939_pgn_table_t *pgn_tab;
	uint8_t pgn_num;

} j1939tp_config_t;

typedef struct
{
	const j1939tp_config_t *config;
	j1939tp_session_t *session;
} j1939tp_runtime_t;

void j1939tp_rx_task(void *arg);
void j1939tp_tx_task(void *arg);
int j1939tp_transmit(j1939tp_runtime_t *runtime, j1939_msg_info_t *info, uint8_t *buffer, uint16_t length);
void on_j1939tp_received(const int channel, const struct can_frame *cf, void *arg);
void on_j1939tp_received_from_isr(const int channel, const struct can_frame *cf, void *arg);
#endif
